<html><head><meta charset="utf-8"><title>07 Webpack 中的模块化开发-慕课专栏</title>
			<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
			<meta name="renderer" content="webkit">
			<meta property="qc:admins" content="77103107776157736375">
			<meta property="wb:webmaster" content="c4f857219bfae3cb">
			<meta http-equiv="Access-Control-Allow-Origin" content="*">
			<meta http-equiv="Cache-Control" content="no-transform ">
			<meta http-equiv="Cache-Control" content="no-siteapp">
			<link rel="apple-touch-icon" sizes="76x76" href="https://www.imooc.com/static/img/common/touch-icon-ipad.png">
			<link rel="apple-touch-icon" sizes="120x120" href="https://www.imooc.com/static/img/common/touch-icon-iphone-retina.png">
			<link rel="apple-touch-icon" sizes="152x152" href="https://www.imooc.com/static/img/common/touch-icon-ipad-retina.png">
			<link href="https://moco.imooc.com/captcha/style/captcha.min.css" rel="stylesheet">
			<link rel="stylesheet" href="https://www.imooc.com/static/moco/v1.0/dist/css/moco.min.css?t=201907021539" type="text/css">
			<link rel="stylesheet" href="https://www.imooc.com/static/lib/swiper/swiper-3.4.2.min.css?t=201907021539">
			<link rel="stylesheet" href="https://static.mukewang.com/static/css/??base.css,common/common-less.css?t=2.5,column/zhuanlanChapter-less.css?t=2.5,course/inc/course_tipoff-less.css?t=2.5?v=201907051055" type="text/css">
			<link charset="utf-8" rel="stylesheet" href="https://www.imooc.com/static/lib/ueditor/themes/imooc/css/ueditor.css?v=201907021539"><link rel="stylesheet" href="https://www.imooc.com/static/lib/baiduShare/api/css/share_style0_16.css?v=6aba13f0.css"></head>
			<body><div id="main">

<div class="container clearfix" id="top" style="display: block; width: 1134px;">
    
    <div class="center_con js-center_con l" style="width: 1134px;">
        <div class="article-con">
                            <!-- 买过的阅读 -->
                <div class="map">
                    <a href="/read" target="_blank"><i class="imv2-feather-o"></i></a>
                    <a href="/read/29" target="_blank">Webpack 从零入门到工程化实战</a>
                    <a href="" target="_blank">
                        <span>
                            / 2-1 07 Webpack 中的模块化开发
                        </span>
                    </a>
                </div>

            


            <div class="art-title" style="margin-top: 0px;">
                07 Webpack 中的模块化开发
            </div>
            <div class="art-info">
                
                <span>
                    更新时间：2019-06-24 09:25:17
                </span>
            </div>
            <div class="art-top">
                                <img src="https://img2.mukewang.com/5cd96312000163ce06400359.jpg" alt="">
                                                <div class="famous-word-box">
                    <img src="https://www.imooc.com/static/img/column/bg-l.png" alt="" class="bg1 bg">
                    <img src="https://www.imooc.com/static/img/column/bg-r.png" alt="" class="bg2 bg">
                    <div class="famous-word">要成就一件大事业，必须从小事做起。<p class="author">——列宁</p></div>
                </div>
                            </div>
            <div class="art-content js-lookimg">
                <div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><strong>模块</strong>是指为了完成某功能所需的程序或者子程序，模块是系统中「职责单一」且「可替换」的部分。所谓的模块化就是指把系统代码分为一系列职责单一且可替换的模块。模块化开发是指如何开发新的模块和复用已有的模块来实现应用的功能。Webpack 作为 JavaScript 模块化打包工具，自然对 JavaScript 的模块化做了不少工作，本文将从模块规范说起，逐渐介绍 Webpack 中对模块化的一些增强处理。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">我们先来认识下三大 JavaScript 主流模块规范：CommonJS、AMD 和 ES6 Module。</p>
</div><div class="cl-preview-section"><h2 id="commonjs" style="font-size: 30px;">CommonJS</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><a href="https://nodejs.org/docs/latest/api/modules.html">CommonJS</a> 规范是 2009 年一名来自 Mozilla 团队的工程师 Kevin Dangoor 开始设计一个叫 ServerJS 的项目提出来的，随着 Node.js 的广泛应用，被广泛接受。通过 <strong>ServerJS</strong> 这个名字就可以知道，CommonJS 主要是服务端用的模块规范。</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token comment">// sayhi.js</span>
<span class="token keyword">var</span> hi <span class="token operator">=</span> <span class="token string">'hello world'</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> hi<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
module<span class="token punctuation">.</span>exports <span class="token operator">=</span> sayHi<span class="token punctuation">;</span>
<span class="token comment">// index.js</span>
<span class="token keyword">var</span> sayHi <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./sayhi.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">上面的代码就是 CommonJS 语法，使用了<code>require</code>来导入一个模块，<code>module.exports</code>导出模块。在 Node.js 中<a href="https://nodejs.org/docs/latest/api/modules.html#modules_the_module_wrapper">实际代码</a>会被处理成下面代码而被应用：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
<span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>exports<span class="token punctuation">,</span> require<span class="token punctuation">,</span> module<span class="token punctuation">,</span> __filename<span class="token punctuation">,</span> __dirname<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// ...</span>
    <span class="token comment">// 模块的代码在这里</span>
    <span class="token comment">// ...</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><h4 id="commonjs-的问题" style="font-size: 26px;">CommonJS 的问题</h4>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">CommonJS 规范是 JavaScript 中最常见的模块格式规范，这一标准的设计初衷是为了让 JavaScript 在多个环境下都实现模块化。起先主要应用在 Node.js 服务端中，但是 Node.js 中的实现依赖了 Node.js 本身功能的实现，包括了 Node.js 的文件系统等，这个规范在浏览器环境是没法使用的。后来随着 <a href="http://browserify.org/">Browserify</a> 和 Webpack 等打包工具的崛起，通过处理的 CommonJS 前端代码也可以在浏览器中使用。</p>
</div><div class="cl-preview-section"><h2 id="amd" style="font-size: 30px;">AMD</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><a href="https://requirejs.org/docs/whyamd.html">AMD 规范</a>是在 CommonJS 规范之后推出的一个解决 web 页面动态异步加载 JavaScript 的规范，相对于 CommonJS 规范，它最大特点是浏览器内支持、实现简单、并且支持异步加载，AMD 规范最早是随着<a href="https://requirejs.org/docs/api.html">RequireJS</a>发展而提出来的，它最核心的是两个方法：</p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;"><code>require()</code>：引入其他模块；</li>
<li style="font-size: 20px; line-height: 38px;"><code>define()</code>：定义新的模块。</li>
</ul>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">基本语法如下：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token comment">// sayhi.js</span>
<span class="token function">define</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">var</span> hi <span class="token operator">=</span> <span class="token string">'hello world'</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">return</span> hi<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// index.js</span>
<span class="token function">require</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'./sayhi.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>sayHi<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">AMD 提出来之后，也有很多变种的规范提出来，比如国内 <a href="https://seajs.github.io/seajs/">Sea.js</a> 的 <a href="https://github.com/cmdjs/specification/blob/master/draft/module.md">CMD</a>，还有兼容 CommonJS 和 AMD 的 <a href="https://github.com/umdjs/umd">UMD 规范</a>（Universal Module Definition）。虽然 AMD 的模式很适合浏览器端的开发，但是随着 npm 包管理的机制越来越流行，这种方式可能会逐步的被淘汰掉。</p>
</div><div class="cl-preview-section"><h3 id="amd-规范的问题">AMD 规范的问题</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 AMD 规范中，我们要声明一个模块，那么需要指定该模块用到的所有依赖项，这些依赖项会被当做形参传到 <code>factory</code>（<code>define</code>方法传入的函数叫做<code>factory</code>）中，对于依赖的模块会提前执行，这种做法叫做 <strong>依赖前置</strong>。依赖前置加大了开发的难度，无论我们在阅读代码还是编写代码的时，都会导致引入的模块内容是条件性执行的。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><strong>而且不管 AMD 还是 CommonJS 都没有统一浏览器和客户端的模块化规范。</strong></p>
</div><div class="cl-preview-section"><h2 id="es6-module" style="font-size: 30px;">ES6 Module</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">ES6 Module，又称为 ES2015 modules，是 ES2015 标准提出来的一种模块加载方式，也是 ECMAScript 官方提出的方案，作为 ES 标准，不仅仅在 Web 现代浏览器（例如 Chrome）上面得到实现，而且在 Node.js 9+ 版本也得到原生支持（需要加上<code>--experimental-modules</code>使用）。</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token comment">// sayhi.js</span>
<span class="token keyword">const</span> hi <span class="token operator">=</span> <span class="token string">'hello world'</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> hi<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// index.js</span>
<span class="token keyword">import</span> sayHi <span class="token keyword">from</span> <span class="token string">'./sayhi'</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">sayHi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">对于前端项目，可以通过 Babel 或者 Typescript 进行提前体验。</p>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：随着主流浏览器逐步开始支持 ES Modules（ESM） 标准，越来越多的目光投注于 Node.js 对于 ESM 的支持实现上；目前 Node.js 使用 CommonJS 作为官方的模块解决方案，虽然内置的模块方案促进了 Node.js 的流行，但是也为引入新的 ES Modules 造成了一定的阻碍。不过 Node.js 9.0+ 已经支持 ESM 语法，需要添加 flag  <code>-experimental-modules</code> 来启动 ESM 语法支持，文件则必须使用 <code>.mjs</code> 后缀： <code>node --experimental-modules some-esm-file.mjs</code> 。</p>
</blockquote>
</div><div class="cl-preview-section"><h2 id="webpack-中一切皆模块" style="font-size: 30px;">Webpack 中一切皆模块</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 Web 前端，我们不仅仅只有 JavaScript，还有 CSS、HTML、图片、字体、富媒体等众多资源，还有一些资源是以类似「方言」的方式存在着，比如 less、sass、各种 js 模板库等，这些资源并不能被直接用在 JavaScript 中，如果在 JavaScript 中像使用模块一样使用，那么可以极大的提高我们的开发体验：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
<span class="token keyword">var</span> img <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./img/webpack.png'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> style <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./css/style.css'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> template <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./template.ejs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这时候，我们就需要 Webpack 了！在 Webpack 中，一切皆模块！</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 Webpack 编译的过程中，Webpack 会要对整个代码进行<strong>静态分析</strong>，分析出各个模块的类型和它们依赖关系，然后将不同类型的模块提交给对应的加载器（loader）来处理。比如一个用 Less 写的样式，可以先用 less-loader 将它转成一个 CSS 模块，然后再通过 css-loader 把他插入到页面的 <code>&lt;style&gt;</code> 标签中执行，甚至还可以通过插件将这部分 CSS 导出为 CSS 文件，使用<code>link</code>标签引入到页面中。</p>
</div><div class="cl-preview-section"><h2 id="webpack-对-module-的增强" style="font-size: 30px;">Webpack 对 Module 的增强</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 Webpack 中，我们不仅可以为所欲为的使用 CommonJS 、AMD 和 ES6 Module 三大规范（比如一个文件中混合使用三种规范），还可以使用 Webpack 对 Module 的增强方法和属性。下面介绍下 Webpack 中特有的一些属性和方法。</p>
</div><div class="cl-preview-section"><h3 id="import和神奇注释"><code>import()</code>和神奇注释</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 Webpack 中，<code>import</code>不仅仅是 ES6 Module 的模块导入方式，还是一个类似<code>require</code>的函数（其实这是 <a href="https://whatwg.github.io/loader/">ES2015 loader 规范</a>的实现），我们可以通过<code>import('path/to/module')</code>的方式引入一个模块，<code>import()</code>返回的是一个<code>Promise</code>对象：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
<span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'path/to/module'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>mod <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>mod<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">下面看看使用<code>import()</code>和 import 的打包有什么区别：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token comment">// hello.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token string">'hello'</span><span class="token punctuation">;</span>
<span class="token comment">// layz.js</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token string">'lazy module'</span><span class="token punctuation">;</span>
<span class="token comment">// index.js</span>
<span class="token keyword">import</span> hello <span class="token keyword">from</span> <span class="token string">'./hello'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'./lazy'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>lazy <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>lazy<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">执行下命令：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">npx webpack <span class="token operator">--</span>mode development：
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><img src="http://img.mukewang.com/5cd8f4e000017d4914440696.png" alt="图片描述" data-original="http://img.mukewang.com/5cd8f4e000017d4914440696.png" class="" style="cursor: pointer;"></p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">通过打包后的 log 和<code>dist</code>文件夹内容发现，我们的代码被分割成了两个文件，一个是<code>main.js</code>一个是<code>0.js</code>，这是因为相对于<code>import from</code>的静态分析打包语法，<code>import()</code>是动态打包语法，即我们的内容不是第一时间打进<code>main.js</code>的，而是通过异步的方式加载进来的。<strong>代码分割是 webpack 进行代码结构组织，实现动态优化的一个重要功能</strong></p>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：与<code>import()</code>用法一样的是<code>require.ensure</code>的方法，这个方法已经被<code>import()</code>方式替换掉；针对<code>import()</code>打包产物跟普通的静态分析打包的实现不同之处，后面原理篇讲解打包产出物的时候会详细介绍。</p>
</blockquote>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">下面我们再来看下<code>import()</code>的<strong>神奇注释</strong>特性，上面<code>index.js</code>的代码修改成下面这样，增加注释<code>webpackChunkName: 'lazy-name'</code></p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token keyword">import</span> hello <span class="token keyword">from</span> <span class="token string">'./hello'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span><span class="token punctuation">(</span>
    <span class="token comment">/*
     webpackChunkName: 'lazy-name'
     */</span>
    <span class="token string">'./lazy'</span>
<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>lazy <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>lazy<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">则打包后的结果，<code>0.js</code>变成了<code>lazy-name.js</code>了，这个文件的名字就是在<code>import()</code>注释里面指定的<code>webpackChunkName</code>，这就是神奇注释（Magic Comments）。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><img src="http://img.mukewang.com/5cd8f51c0001942a14440696.png" alt="图片描述" data-original="http://img.mukewang.com/5cd8f51c0001942a14440696.png" class="" style="cursor: pointer;"></p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">目前支持的注释有：</p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;"><code>webpackInclude</code>：如果是 import 的一个目录，则可以指定需要引入的文件特性，例如只加载 json 文件：<code>/\.json$/</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>webpackExclude</code>：如果是 import 的一个目录，则可以指定需要过滤的文件，例如 <code>/\.noimport\.json$/</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>webpackChunkName</code>：这是 chunk 文件的名称，例如 <code>lazy-name</code>；</li>
<li style="font-size: 20px; line-height: 38px;"><code>webpackPrefetch</code>: 是否预取模块，及其优先级，可选值<code>true</code>、或者整数优先级别，0 相当于 true，webpack 4.6+支持；</li>
<li style="font-size: 20px; line-height: 38px;"><code>webpackPreload</code>: 是否预加载模块，及其优先级，可选值<code>true</code>、或者整数优先级别，0 相当于 true，webpack 4.6+支持；</li>
<li style="font-size: 20px; line-height: 38px;"><code>webpackMode</code>: 可选值<code>lazy</code>/<code>lazy-once</code>/<code>eager</code>/<code>weak</code>。</li>
</ul>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这里最复杂的是<code>webpackMode</code>：</p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;"><code>lazy</code>：是默认的模式，为每个 <code>import()</code> 导入的模块，生成一个可延迟加载 chunk；</li>
<li style="font-size: 20px; line-height: 38px;"><code>lazy-once</code>：生成一个可以满足所有 <code>import()</code> 调用的<strong>单个</strong>可延迟加载 chunk，此 chunk 将在第一次 import() 调用时获取，随后的 import() 调用将使用相同的网络响应；注意，这种模式仅在部分动态语句中有意义，例如 import(<code>./locales/${language}.json</code>)，其中可能含有多个被请求的模块路径；</li>
<li style="font-size: 20px; line-height: 38px;"><code>eager</code>：不会生成额外的 chunk，所有模块都被当前 chunk 引入，并且没有额外的网络请求。仍然会返回 Promise，但是是 resolved 状态。和静态导入相对比，在调用 <code>import()</code> 完成之前，该模块不会被执行。</li>
<li style="font-size: 20px; line-height: 38px;"><code>weak</code>：尝试加载模块，如果该模块函数已经以其他方式加载（即，另一个 chunk 导入过此模块，或包含模块的脚本被加载）。仍然会返回 Promise，但是只有在客户端上已经有该 chunk 时才成功解析。如果该模块不可用，Promise 将会是 rejected 状态，并且网络请求永远不会执行。当需要的 chunks 始终在（嵌入在页面中的）初始请求中手动提供，而不是在应用程序导航在最初没有提供的模块导入的情况触发，这对于 Server 端渲染（SSR，Server-Side Render）是非常有用的。</li>
</ul>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">通过上面的神奇注释，<code>import()</code>不再是简单的 JavaScript 异步加载器，还是任意模块资源的加载器，举例说明：如果我们页面用到的图片都放在<code>src/assets/img</code>文件夹下，你们可以通过下面方式将用到的图片打包到一起：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
<span class="token keyword">import</span><span class="token punctuation">(</span><span class="token comment">/* webpackChunkName: "image", webpackInclude: /\.(png|jpg|gif)/ */</span> <span class="token string">'./assets/img'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：prefetch 优先级低于 preload，preload 会并行或者加载完主文件之后立即加载；prefetch 则会在主文件之后、空闲时在加载。prefetch 和 preload 可以用于提前加载图片、样式等资源的功能。</p>
</blockquote>
</div><div class="cl-preview-section"><h3 id="require.resolve-和"><code>require.resolve()</code> 和</h3>
</div><div class="cl-preview-section"><h3 id="require.resolveweak"><code>require.resolveWeak()</code></h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>require.resolve()</code> 和 <code>require.resolveWeak()</code>都可以获取模块的唯一 ID（moduleId），区别在于<code>require.resolve()</code>会把模块真实引入进 bundle，而<code>require.resolveWeak()</code>则不会，配合<code>require.cache</code>和 <code>__webpack_modules__</code>可以用于判断模块是否加载成功或者是否可用。</p>
</div><div class="cl-preview-section"><h3 id="require.context"><code>require.context()</code></h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>require.context(directory, includeSubdirs, filter)</code>可以批量将<code>directory</code>内的文件全部引入进文件，并且返回一个具有<code>resolve</code>的 context 对象，使用<code>context.resolve(moduleId)</code>则返回对应的模块。</p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;">directory：目录 string；</li>
<li style="font-size: 20px; line-height: 38px;">includeSubdirs：是否包含子目录，可选，默认值是 true；</li>
<li style="font-size: 20px; line-height: 38px;">filter：过滤正则规则，可选项。</li>
</ul>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：注意 <code>require.context()</code> 会将所有的文件都引入进 bundle！</p>
</blockquote>
</div><div class="cl-preview-section"><h3 id="require.include"><code>require.include()</code></h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><code>require.include(dependency)</code>顾名思义为引入某个依赖，但是并不执行它，可以用于优化 chunk，例如下面示例代码：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
require<span class="token punctuation">.</span><span class="token function">include</span><span class="token punctuation">(</span><span class="token string">'./hello.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
require<span class="token punctuation">.</span><span class="token function">ensure</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'./hello.js'</span><span class="token punctuation">,</span> <span class="token string">'./weak.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>require<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">/* ... */</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
require<span class="token punctuation">.</span><span class="token function">ensure</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'./hello.js'</span><span class="token punctuation">,</span> <span class="token string">'./lazy.js'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>require<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">/* ... */</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">上面代码打包之后结果：</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><img src="http://img.mukewang.com/5cd8f55700010a9714440792.png" alt="图片描述" data-original="http://img.mukewang.com/5cd8f55700010a9714440792.png" class="" style="cursor: pointer;"></p>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;">main 包含了 hello 和 index；</li>
<li style="font-size: 20px; line-height: 38px;">weak 和 lazy 分别被打包到 1，0 两个文件。</li>
</ul>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">这实际上使用了<code>require.include()</code>直接优化了代码分割，如果不用<code>require.include('./hello.js');</code>则<code>hello.js</code>会分别和<code>weak</code>、<code>lazy</code>打包，注意下面打包 log 的<code>[./src/hello.js] 24 bytes {0} {1} [built]</code> 说明<code>hello.js</code>被打包进了 0，1 两个文件。</p>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;"><img src="http://img.mukewang.com/5cd8f59a00015e9f14440792.png" alt="图片描述" data-original="http://img.mukewang.com/5cd8f59a00015e9f14440792.png" class="" style="cursor: pointer;"></p>
</div><div class="cl-preview-section"><h3 id="resourcequery"><code>__resourceQuery</code></h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">当前模块的资源查询（resource query），即当前模块引入是传入的 query 信息，例如：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token comment">// main.js</span>
<span class="token keyword">const</span> component <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./component-loader?commponent=demo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// component-loader.js</span>
<span class="token keyword">const</span> querystring <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'querystring'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> query <span class="token operator">=</span> querystring<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>__resourceQuery<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 去掉?</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>query<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// {component: demo}</span>
</code></pre>
</div><div class="cl-preview-section"><h3 id="其他">其他</h3>
</div><div class="cl-preview-section"><ul>
<li style="font-size: 20px; line-height: 38px;"><strong>webpack_public_path</strong>：等同于 <code>output.publicPath</code> 配置选项；</li>
<li style="font-size: 20px; line-height: 38px;"><strong>webpack_require</strong>：原始 <code>require</code> 函数。这个表达式不会被解析器解析为依赖。</li>
<li style="font-size: 20px; line-height: 38px;"><strong>webpack_chunk_load</strong>：内部 <code>chunk</code> 载入函数，用法<code>__webpack_chunk_load__(chunkId, callback(require))</code>；
<ul>
<li style="font-size: 20px; line-height: 38px;">chunkId ：需要载入的 chunk id</li>
<li style="font-size: 20px; line-height: 38px;">callback(require)： chunk 载入后调用的回调函数。</li>
</ul>
</li>
<li style="font-size: 20px; line-height: 38px;"><strong>webpack_modules</strong>：所有模块的内部对象，可以通过传入 moduleId 来获取对应的模块；<code>require.resolve()</code> 和 <code>require.resolveWeak()</code>获取<code>moduleId</code>；</li>
<li style="font-size: 20px; line-height: 38px;">module.hot：用于判断是否在 hotModuleReplace 模式下，一般可以用于 loader 编写中判断在 HMR 模式下增加 reload 逻辑代码等；</li>
<li style="font-size: 20px; line-height: 38px;"><strong>webpack_hash</strong>：这个变量只有在启用 <code>HotModuleReplacementPlugin</code> 或者 <code>ExtendedAPIPlugin</code> 时才生效。这个变量提供对编译过程中(compilation)的 hash 信息的获取。</li>
<li style="font-size: 20px; line-height: 38px;"><strong>non_webpack_require</strong>：生成一个不会被 webpack 解析的 <code>require</code> 函数</li>
</ul>
</div><div class="cl-preview-section"><h3 id="webpack-对-node.js-模块的-polyfill">Webpack 对 Node.js 模块的 polyfill</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">Webpack 还对一些常用的 Node.js 模块和属性进行了 mock，例如在 web 的 js 文件中可以直接引入 Node.js 的<code>querystring</code>模块，这个模块实际引入的是<a href="https://github.com/webpack/node-libs-browser">node-libs-browser</a>来对 Node.js 核心库 polyfill，详细在 web 页面中可以用的 Node.js 模块，可以参考<a href="https://github.com/webpack/node-libs-browser">node-libs-browser</a> Readme 文件的表格。</p>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">polyfill：英文原意为一种用于衣物、床具等的聚酯填充材料，例如装修时候的腻子，作用是抹平坑坑洼洼的墙面；在 JavaScript 中表示一些可以抹平浏览器实现差异的代码，比如某浏览器不支持 Promise，可以引入<code>es6-promise-polyfill</code>等库来解决。</p>
</blockquote>
</div><div class="cl-preview-section"><h2 id="webpack-对资源的模块化处理" style="font-size: 30px;">Webpack 对资源的模块化处理</h2>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">下面在讲解下 Webpack 对其他资源的模块化处理方案。</p>
</div><div class="cl-preview-section"><h3 id="样式文件的import和">样式文件的<code>@import</code>和</h3>
</div><div class="cl-preview-section"><h3 id="javascript-中的import">JavaScript 中的<code>import</code></h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 Webpack 中的 css （包括其预处理语言，例如 Less、Sass）等，都可以在内部通过<code>@import</code>语法直接引入使用：</p>
</div><div class="cl-preview-section"><pre class="  language-css"><code class="prism  language-css"><span class="token atrule"><span class="token rule">@import</span> <span class="token string">'vars.less'</span><span class="token punctuation">;</span></span>
<span class="token selector">body </span><span class="token punctuation">{</span>
    <span class="token property">background</span><span class="token punctuation">:</span> <span class="token atrule"><span class="token rule">@bg-color</span><span class="token punctuation">;</span></span>
<span class="token punctuation">}</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">除了样式文件中的<code>@import</code>，在 JavaScript 文件中，还支持直接使用 ES6 Module 的<code>import</code>（包括 <code>require</code>）直接引入样式文件，例如：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js"><span class="token keyword">import</span> styles <span class="token keyword">from</span> <span class="token string">'./style.css'</span><span class="token punctuation">;</span>
</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">JavaScript 的这种语法其实是 <a href="https://github.com/css-modules/css-modules">CSS Modules 语法</a>，目前浏览器支持程度有限，但是在 Webpack 中，我们可以通过配置 loader 优先使用这种方式！</p>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：后面讲解 CSS 样式配置的时候会更加详细的讲解 CSS Modules。</p>
</blockquote>
</div><div class="cl-preview-section"><h3 id="使用-loader-把资源作为模块引入">使用 loader 把资源作为模块引入</h3>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">在 Webpack 中，除了 CSS 可以直接使用<code>import</code>语法引入，类似 HTML 和页面模板等，可以直接使用对应的 loader ，通过下面的语法来引入：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
<span class="token keyword">const</span> html <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'html-loader!./loader.html'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>html<span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">上面的代码得到<code>html</code>变量实际为引入的<code>loader.html</code>的 string 片段。除了 html，类似模板文件，还可以直接转换为对应的 render 函数，例如下面代码使用了<a href="https://www.npmjs.com/package/ejs-loader">ejs-loader</a>：</p>
</div><div class="cl-preview-section"><pre class="  language-js"><code class="prism  language-js">
<span class="token keyword">const</span> render <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'ejs!./file.ejs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// =&gt; 得到 ejs 编译后的 render 函数</span>
<span class="token function">render</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 传入 data，直接返回的是 html 片段</span>

</code></pre>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">Tips：原理篇将动手手写一个  <code>markdown-loader</code> ，更深入的了解其功能和原理实现。</p>
</blockquote>
</div><div class="cl-preview-section"> <div class="summary"><h5 class="centertitle" style="font-size: 20px; line-height: 38px;">小结 </h5></div><!--title:&#23567;&#32467; -->
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">本小节从 JavaScript 的模块化发展史讲起，逐渐介绍了应用到服务端的 CommonJS 规范、浏览端的 AMD 规范和 ES6 Modules 规范。在 Webpack 中一切皆模块，任何资源都可以被当做模块引入进来，不仅仅是 JavaScript 模块和 Node.js 的模块，甚至 CSS、Less、JavaScript 模板、图片等任何资源，只需要配合对应的 loader 就可以实现资源的引入。本小节介绍的按需加载和神奇注释，在日常项目优化中经常使用。</p>
</div><div class="cl-preview-section"><blockquote>
<p style="font-size: 20px; line-height: 38px;">本小节 Webpack 相关面试题：</p>
<p style="font-size: 20px; line-height: 38px;">1.什么是 JavaScript 的模块化开发？有哪些可以遵循的规范？<br>
2.在 js 文件中怎么调用 loader 来处理一个模块？<br>
3.Webpack 中怎么获取一个模块引用另外一个模块是传入的 query？<br>
4.怎么实现 Webpack 的按需加载？什么是神奇注释？</p>
</blockquote>
</div><div class="cl-preview-section"><p style="font-size: 20px; line-height: 38px;">专栏代码已经整理好给大家共享出来：<br>
<a href="https://github.com/ksky521/webpack-tutorial">https://github.com/ksky521/webpack-tutorial</a></p>
</div></div>
            </div>
                            <!-- 买过的阅读 -->
                <div class="art-next-prev clearfix">
                                                                        <!-- 已买且开放 或者可以试读 -->
                            <a href="/read/29/article/264">
                                                    <div class="prev l clearfix">
                                <div class="icon l">
                                    <i class="imv2-arrow3_l"></i>
                                </div>
                                <p>
                                    06 基础概念和常见配置项介绍（二）
                                </p>
                            </div>
                        </a>
                                                                                            <!-- 已买且开放 或者可以试读 -->
                            <a href="/read/29/article/266">
                                                    <div class="next r clearfix">
                                <p>
                                    08 在 Webpack 中使用 Babel 转换 JavaScript 代码
                                </p>
                                <div class="icon r">
                                    <i class="imv2-arrow3_r"></i>
                                </div>

                            </div>
                        </a>
                                    </div>
                    </div>
        <div class="comments-con js-comments-con" id="coments_con">
        </div>



    </div>
    
    
    

</div>
 
<!-- 专栏介绍页专栏评价 -->

<!-- 专栏介绍页底部三条评价 -->

<!-- 专栏阅读页弹层目录和介绍页页面目录 -->

<!-- 专栏阅读页发布回复 -->

<!-- 专栏阅读页发布评论 -->

<!-- 专栏阅读页底部评论 -->

<!-- 专栏阅读 单个 评论 -->

<!-- 新增回复和展开三条以外回复 -->

<!-- 立即订阅的弹窗 -->












</div></body></html>